﻿using System;
using System.Security.Cryptography;
using System.Text;

namespace blockchain_sim
{
    class Program
    {
        static void ShowMenu()
        {
            Console.WriteLine("区块链模拟器");
            Console.WriteLine("clear :清除显示区域");
            Console.WriteLine("show :显示所有区块内容");
            Console.WriteLine("add xxx :用xxx为内容");
            Console.WriteLine("exit :退出此程序");
        }
        static void Main(string[] args)
        {
            ShowMenu();
            while (true)
            {
                var line = Console.ReadLine();
                if (line.Length == 0)
                    continue;

                var words = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
                if (words.Length == 0)
                    continue;

                var cmd = words[0].ToLower();
                if (cmd == "?" || cmd == "？" || cmd == "help")
                {
                    ShowMenu();
                    continue;
                }
                if (cmd == "clear")
                {
                    Console.Clear();
                    continue;
                }
                if (cmd == "show")
                {
                    ShowBlockChain();
                    continue;
                }
                if (cmd == "add")
                {
                    if (words.Length < 2)
                    {
                        Console.WriteLine("使用方法不对，请参考\"add xxx\"");
                        continue;
                    }
                    AddBlock(words[1]);
                    continue;
                }
                if (cmd == "exit")
                {
                    break;
                }

                Console.WriteLine("unknown cmd;");
                ShowMenu();
            }
        }
        static UInt32 BlockHeight;
        static bool bInit = false;
        static SHA256 sha256 = SHA256.Create();
        static void ShowBlockChain()
        {
            Console.WriteLine("====显示区块信息区块====");

            var path = "blockdata" + System.IO.Path.DirectorySeparatorChar;
            if (System.IO.Directory.Exists(path) == false)
            {
                System.IO.Directory.CreateDirectory(path);
            }
            byte[] lastdata = null;
            for (UInt32 i = 0; i < UInt32.MaxValue; i++)
            {
                var filename = path + "block_" + i.ToString("X08") + ".txt";
                if (System.IO.File.Exists(filename) == false)//找不到这个文件，到此为止
                {
                    if (i == 0)
                    {
                        Console.WriteLine("没有任何区块,初始化");
                        System.IO.File.WriteAllText(filename, "这是创世区块", System.Text.Encoding.UTF8);
                        bInit = true;
                    }
                    else
                    {
                        Console.WriteLine("当前最高区块:" + BlockHeight);
                    }
                    return;
                }
                BlockHeight = i;
                bInit = true;

                var file = System.IO.File.ReadAllText(filename, System.Text.Encoding.UTF8);
                if (i == 0)
                {//创世区块，他没有上一个区块

                }
                else
                {//检查上一个区块是否合法
                    var lines = file.Split("\r\n");
                    var lasthash = lines[0];
                    var lasthashcheck = Hex2Str(sha256.ComputeHash(lastdata));
                    if (string.Equals(lasthash, lasthashcheck) == false)
                    {
                        Console.WriteLine("高度=" + i + "时发现数据被篡改");
                        bInit = false;
                        return;
                    }
                }

                lastdata = System.Text.Encoding.UTF8.GetBytes(file);
            }
        }
        static string Hex2Str(byte[] data)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var b in data)
            {
                sb.Append(b.ToString("X02"));
            }
            return sb.ToString();
        }
        static byte[] Str2Hex(string hashstr)
        {
            byte[] data = new byte[hashstr.Length / 2];
            for (var i = 0; i < data.Length; i++)
            {
                data[i] = byte.Parse(hashstr.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
            }
            return data;
        }
        static void AddBlock(string txt)
        {
            Console.WriteLine("====增加区块====");
            if (!bInit)
            {
                ShowBlockChain();
            }
            if(!bInit)
            {
                return;
            }

            var path = "blockdata" + System.IO.Path.DirectorySeparatorChar;
            if (System.IO.Directory.Exists(path) == false)
            {
                System.IO.Directory.CreateDirectory(path);
            }

            var lastfilename = path + "block_" + BlockHeight.ToString("X08") + ".txt";
            var lastfile = System.IO.File.ReadAllText(lastfilename, System.Text.Encoding.UTF8);
            byte[] lastdata = System.Text.Encoding.UTF8.GetBytes(lastfile);

            var filename = path + "block_" + (BlockHeight + 1).ToString("X08") + ".txt";
            System.IO.File.Delete(filename);

            var hash = sha256.ComputeHash(lastdata);
            var newdata = Hex2Str(hash) + "\r\n" + txt;
            System.IO.File.WriteAllText(filename, newdata, System.Text.Encoding.UTF8);

            ShowBlockChain();
        }
    }
}
